import CryptoJS from 'crypto-js';

const ASE_KEY = '202CB962AC59075B964B07152D234B70'; // 加密key
const ASE_IV = '233FA6B19FDE8CAF'; // 加密iv
const SECURITY_KEY = 'D9840773233FA6B19FDE8CAF765402F5'; // 安全key

interface Encrypted {
  bytes: any; // 根据实际加密库返回的字节数据类型进行准确替换
}

/**
 * 加密数据的函数。
 *
 * @param {any} data - 需要加密的数据。
 * @returns {Promise<string>} 返回一个Promise，解析为加密后的数据。
 */
export async function encryptData(data: any): Promise<string> {
  // 创建一个空对象来存储参数
  let params: Record<string, any> = {};
  // 如果传入的数据是对象，则将其属性复制到params对象中
  if (typeof data === 'object' && data !== null) {
    params = { ...data };
  }
  // 获取当前时间戳
  const timestamp = Math.floor(Date.now() / 1000);
  // 生成一个随机字符串
  const nonce: string = await generateNonce();
  // 将时间戳和随机字符串添加到参数中
  params['timestamp'] = timestamp;
  params['nonce_str'] = nonce;
  // 对参数进行排序
  const sortedParams = sort(params);
  // 构建参数字符串
  const paramsValue: string = `${sortedParams}&key=${SECURITY_KEY}`;
  // 计算参数字符串的MD5哈希值
  // 将MD5哈希值添加到参数中
  params['sign'] = CryptoJS.MD5(paramsValue).toString().toUpperCase();
  // 使用AES算法加密参数
  const encrypted: Encrypted = encryptWithAES(JSON.stringify(params), ASE_KEY, ASE_IV);
  // 将加密后的数据转换为十六进制字符串
  // 返回加密后的数据
  return CryptoJS.enc.Hex.stringify(encrypted.bytes);
}

/**
 * 生成一个随机字符串。
 *
 * @returns {Promise<string>} 返回一个Promise，解析为生成的随机字符串。
 */
async function generateNonce(): Promise<string> {
  return new Promise((resolve) => {
    if (typeof uni.getRandomValues === 'function') {
      uni.getRandomValues({
        length: 16,
        success: (res) => {
          resolve(uni.arrayBufferToBase64(res.randomValues));
        }
      });
    } else if ( window.crypto && typeof window.crypto.getRandomValues === 'function') {
      const array = new Uint8Array(16);
      window.crypto.getRandomValues(array);
      resolve(array.toString());
    }
  });
}

/**
 * 使用AES算法加密数据。
 *
 * @param {string} data - 需要加密的数据。
 * @param {string} key - 加密密钥。
 * @param {string} iv - 初始化向量。
 * @returns {Encrypted} 返回加密后的数据对象。
 */
function encryptWithAES(data: string, key: string, iv: string): Encrypted {
  // 将密钥转换为WordArray对象，用于AES加密
  const keyWordArray = CryptoJS.enc.Utf8.parse(key);
  // 将初始化向量转换为WordArray对象，用于AES加密
  const ivWordArray = CryptoJS.enc.Utf8.parse(iv);
  // 使用AES算法加密数据
  const encrypted = CryptoJS.AES.encrypt(data, keyWordArray, {
    iv: ivWordArray,
    mode: CryptoJS.mode.CBC,
    padding: CryptoJS.pad.Pkcs7
  });
  // 返回加密后的数据对象
  return {
    bytes: encrypted.ciphertext
  };
}

/**
 * 对 JSON 对象进行排序并转换为字符串。
 *
 * @param {Record<string, any>} json - 需要排序的 JSON 对象。
 * @returns {string} 返回排序后的字符串。
 */
function sort(json: Record<string, any>): string {
  // 获取 JSON 对象的所有键，并按字母顺序排序
  const keys: string[] = Object.keys(json).sort();
  // 创建一个空对象来存储排序后的键值对
  const result: { [key: string]: any } = {};

  // 遍历排序后的键
  for (const key of keys) {
    const value = json[key];
    // 如果值是对象且不是数组，则递归调用_sort函数进行排序
    if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
      result[key] = sort(value as { [key: string]: any });
      // 如果值是数组，则遍历数组中的每个元素
    } else if (Array.isArray(value)) {
      const processedArray = value!.map((e: any) => {
        // 如果元素是对象，则递归调用_sort函数进行排序
        if (typeof e === 'object' && e !== null) {
          return sort(e as { [key: string]: any });
        }
        // 如果元素不是对象，则直接返回元素
        return e;
      });
      // 将处理后的数组转换为字符串，并使用'&'作为分隔符
      result[''] = processedArray.join('&');
      // 如果值不是对象或数组，则直接将键值对添加到result对象中
    } else {
      result[key] = value;
    }
  }

  // 将result对象转换为字符串，并使用'&'作为键值对之间的分隔符，'='作为键和值之间的分隔符
  return Object.entries(result)
    .map(([mapKey, mapValue]) => `${mapKey}${mapKey === '' ? '' : '='}${mapValue}`)
    .join('&');
}

/**
 * 使用AES算法解密数据。
 *
 * @param {string} data - 待解密的字符串数据。
 * @returns {object} 解密后的数据对象。
 */
export function decryptData(data: string): { [key: string]: any } {
  // 将ASE_KEY转换为WordArray对象，用于AES解密
  const keyWordArray = CryptoJS.enc.Utf8.parse(ASE_KEY);
  // 将ASE_IV转换为WordArray对象，用于AES解密
  const ivWordArray = CryptoJS.enc.Utf8.parse(ASE_IV);
  // 将十六进制字符串转换为WordArray对象
  const bytes = CryptoJS.enc.Hex.parse(data);
  // 使用AES算法解密数据
  const decrypted = CryptoJS.AES.decrypt(
    CryptoJS.lib.CipherParams.create({ ciphertext: bytes }),
    keyWordArray,
    {
      iv: ivWordArray,
      mode: CryptoJS.mode.CBC,
      padding: CryptoJS.pad.Pkcs7
    }
  );
  // 将解密后的数据转换为 UTF-8 字符串
  const value = decrypted.toString(CryptoJS.enc.Utf8);
  // 将字符串解析为JSON对象并返回
  return JSON.parse(value);
}
